import json
import os

from openpyxl import load_workbook
from cached_property import cached_property

from settings import FILES_DIR, RESULTS_DIR
from stack import (StackNumberWrong, StraightStack, VerticalStack,
                   parse_stack_number)


LINE_RADIUS = 100000

"""
该文件用来读取直曲表和竖曲表，并返回所有原始桩
"""


class WorkSheet:
    stack_row_index_start: str = None
    primary_column: str = None
    metas = ['title', 'project_name']

    def __init__(self, filename: str, output_name=None, reverse=False):
        self.filepath = filename if os.path.isabs(
            filename) else os.path.join(FILES_DIR, filename)
        self.wb = load_workbook(filename=self.filepath, read_only=True)
        self.filename = os.path.split(filename)[-1]
        self.reverse = reverse
        self.sheet = self.wb[self.wb.sheetnames[0]]
        self.title = self.sheet['A1'].value
        self.project_name = self.sheet['A2'].value
        self.output_name = output_name
        os.makedirs(os.path.join(self.path, 'middle_data'), exist_ok=True)

    @cached_property
    def rows(self):
        rows = []
        row_index = self.stack_row_index_start
        blanks = 0
        while blanks <= 3:
            try:
                row = self.parse_row(row_index)
            except StackNumberWrong:
                row = None
            if row is None:
                blanks += 1
            else:
                rows.append(row)
            row_index = str(int(row_index)+2)
        with open(os.path.join(self.path, 'middle_data', f"{self.filename.replace('.xlsx','.json')}"), mode='w') as file:
            json.dump(rows, file)
        return rows

    def parse_row(self, row_index):
        raise NotImplementedError

    @property
    def stacks(self):
        raise NotImplementedError

    @cached_property
    def path(self):
        dirname = '下行' if self.reverse else '上行'
        output_name = self.project_name.split(
        )[0] if self.output_name is None else self.output_name
        return os.path.join(RESULTS_DIR, output_name, dirname)


class StraightWorkSheet(WorkSheet):
    primary_column = 'D'
    stack_row_index_start = '9'

    def parse_row(self, row_index: str):
        sheet = self.sheet
        row_index = str(row_index)
        row_index_latter = str(int(row_index)+1)
        if sheet[self.primary_column+row_index].value is None:
            return None
        intersection_number = sheet['A'+row_index].value  # 交点号
        intersection_coordinate = {
            'N': sheet['B'+row_index].value,  # 交点坐标N（X）
            'E': sheet['C'+row_index].value  # 交点坐标E（Y）
        }
        stack_number = sheet['D'+row_index].value  # 桩号
        parse_stack_number(stack_number)
        corner_value = sheet['E'+row_index].value  # 转角值
        curve_factors = {
            'radius': sheet['F'+row_index].value or LINE_RADIUS,  # 半径
            # 缓和曲线长
            'transition_curve_length': sheet['G'+row_index].value,
            # 缓和曲线参数
            'transition_curve_argument': sheet['H'+row_index].value,
            'tangent_length': sheet['I'+row_index].value,  # 切线长
            'curve_length': sheet['J'+row_index].value,  # 曲线长
            'out_distance': sheet['K'+row_index].value,  # 外距
            'correction_value': sheet['L'+row_index].value  # 校准值
        }
        curve_main_point_stack_numbers = {
            # 第一缓和曲线起点
            'first_transition_curve_start': sheet['M'+row_index].value,
            # 第一缓和曲线终点
            'first_transition_curve_end': sheet['N'+row_index].value,
            'circular_curve_start': sheet['N'+row_index].value,  # 圆曲线起点
            'curve_middle': sheet['O'+row_index].value,  # 曲线中点
            # 第二缓和曲线起点
            'second_transition_curve_start': sheet['P'+row_index].value,
            'circular_curve_end': sheet['P'+row_index].value,  # 圆曲线终点
            # 第二缓和曲线终点
            'second_transition_curve_end': sheet['Q'+row_index].value
        }
        line_length = sheet['R'+row_index_latter].value  # 直线段长
        intersection_spacing = sheet['S'+row_index_latter].value  # 交点间距
        computed_azimuth_angle = sheet[
            'T'+row_index_latter].value  # 计算方位角
        remark = sheet['U'+row_index].value  # 备注
        return {
            'intersection_number': intersection_number,
            'intersection_coordinate': intersection_coordinate,
            'stack_number': stack_number,
            'corner_value': corner_value,
            'curve_factors': curve_factors,
            'curve_main_point_stack_numbers': curve_main_point_stack_numbers,
            'line_length': line_length,
            'intersection_spacing': intersection_spacing,
            'computed_azimuth_angle': computed_azimuth_angle,
            'remark': remark
        }

    @cached_property
    def stacks(self):
        stacks = []
        for row in self.rows:
            if row['curve_factors']['radius'] is None:
                continue
            try:
                if row['curve_main_point_stack_numbers']['first_transition_curve_start'] is not None:
                    stacks.append(StraightStack(
                        row['curve_main_point_stack_numbers']['first_transition_curve_start'], radius=row['curve_factors']['radius']))
                    stacks.append(StraightStack(
                        row['curve_main_point_stack_numbers']['second_transition_curve_end'], radius=LINE_RADIUS))
                else:
                    stacks.append(StraightStack(
                        row['curve_main_point_stack_numbers']['circular_curve_start'], radius=row['curve_factors']['radius']))
                    stacks.append(StraightStack(
                        row['curve_main_point_stack_numbers']['circular_curve_end'], radius=row['curve_factors']['radius']))
            except StackNumberWrong:
                continue
        return stacks


class VerticalWorkSheet(WorkSheet):
    primary_column = 'B'
    stack_row_index_start = '5'

    def parse_row(self, row_index: str):
        sheet = self.sheet
        row_index = str(row_index)
        row_index_latter = str(int(row_index)+1)
        if sheet[self.primary_column+row_index].value is None:
            return None
        order_number = sheet['A'+row_index].value  # 序号
        stack_number = sheet['B'+row_index].value  # 桩号
        parse_stack_number(stack_number)
        elevation = sheet['C'+row_index].value  # 标高
        convex_curve_radius = sheet['D'+row_index].value  # 凸曲线半径
        concave_curve_radius = sheet['E'+row_index].value  # 凹曲线半径
        tangent_length = sheet['F'+row_index].value  # 切线长
        out_distance = sheet['G'+row_index].value  # 外距
        start_stack_number = sheet['H'+row_index].value  # 起点桩号
        end_stack_number = sheet['I'+row_index].value  # 终点桩号
        slope_degree = sheet[
            'J'+row_index_latter].value or sheet['K'+row_index_latter].value  # 坡度
        knick_point_spacing = sheet[
            'L'+row_index_latter].value  # 变坡点间距
        straight_slope_length = sheet[
            'M'+row_index_latter].value  # 直坡段长
        remark = sheet['N'+row_index].value  # 备注

        return {
            'order_number': order_number,
            'stack_number': stack_number,
            'elevation': elevation,
            'convex_curve_radius': convex_curve_radius,
            'concave_curve_radius': concave_curve_radius,
            'tangent_length': tangent_length,
            'out_distance': out_distance,
            'start_stack_number': start_stack_number,
            'end_stack_number': end_stack_number,
            'slope_degree': slope_degree,
            'knick_point_spacing': knick_point_spacing,
            'straight_slope_length': straight_slope_length,
            'remark': remark
        }

    @cached_property
    def stacks(self):
        stacks = []
        for row in self.rows:
            try:
                stacks.append(VerticalStack(
                    row['stack_number'], slope_degree=row['slope_degree']))
            except StackNumberWrong as e:
                raise e
        return stacks
